/*
 * CLitePlayer.cpp
 *
 *  Created on: 2016年3月17日
 *      Author: terry
 */

#include "CLitePlayer.h"
#include "CVideoRender.h"
#include "CAudioRender.h"
#include "TSafeStr.h"
#include "MediaHelper.h"
#include "CMediaSourceFactory.h"
#include "CVideoRender.h"
#include "CAudioRender.h"
#include "CVideoDecoder.h"
#include "CAudioDecoder.h"
#include "CLog.h"

#include "AppProperties.h"
#include "Utility.h"
#include "CFileMediaSink.h"
#include "TSFileMediaSink.h"
#include "PacketFileMediaSink.h"

#include "MediaProp.h"
#include "FfmpegUtil.h"
#include "CVideoFrameFilter.h"
#include <math.h>
#include "TStringUtil.h"
#include "TimeHelper.h"
#include "BasicMacro.h"
#include "TimePoint.h"
#include "AVFrameUtil.h"
#include "LitePlayerCore.h"


namespace av
{


static const int MAX_SYNC_TIME = 2 * 1000 * 1000;
static const AVRational PREREAD_SCALE = av_make_q(5,2);



CLitePlayer::CLitePlayer():
		m_url(),
		m_params(),
		m_state(),
		m_fmt(),
		m_scale(av_make_q(1,1)),
		m_packetSink(),
		m_eventSink(),
		m_isLive(),
		m_waitForKeyFrame(),
        m_videoFrameCount(),
		m_curTime()
{
	Utility::copyProp(AppProperties::getProperties(), m_params);

	m_sourceFactory.reset(new CMediaSourceFactory());

	m_videoRender.reset(new CVideoRender());
	m_audioRender.reset(new CAudioRender());

	m_videoRender->setRefClock(m_audioRender.get());
	
}

CLitePlayer::~CLitePlayer()
{
	close();
}

int CLitePlayer::open(const char* url, const char* params)
{
	if ((url == NULL) || (strlen(url) <= 0))
	{
		return EINVAL;
	}

	if (isOpen())
	{
		close();
	}

	m_url = url;

    assert(isRunning() == false);
	util::CommandThread::start();

	postCommand(kOpen);

	return 0;
}

void CLitePlayer::close()
{
	int state = getState();
	if (state != STATE_STOPPED)
	{
		stop();
	}

	if (isOpen())
	{
		postCommand(kClose);
	}

	util::CommandThread::stop();

	m_url.clear();
	m_format.clear();
}

bool CLitePlayer::isOpen()
{
	return isRunning();
}

const char* CLitePlayer::getURL()
{
	return m_url.c_str();
}

bool CLitePlayer::getFormat(MediaFormat& fmt)
{
	comn::AutoCritSec lock(m_cs);
	fmt = m_format;
	return true;
}

int CLitePlayer::getDuration()
{
	comn::AutoCritSec lock(m_cs);
	if (m_source)
	{
		return m_source->getDuration();
	}
	return 0;
}

int CLitePlayer::play()
{
	postCommand(kPlay);
	return 0;
}

int CLitePlayer::pause()
{
	postCommand(kPause);
	return 0;
}

void CLitePlayer::stop()
{
	postCommand(kStop);

}

int CLitePlayer::getState()
{
	return m_state;
}


bool CLitePlayer::seekable()
{
	return m_source->seekable();
}

int CLitePlayer::seek(int64_t offset)
{
	offset = offset * 1000;

	if (offset < 0)
	{
		return EINVAL;
	}

	{
		comn::AutoCritSec lock(m_cs);
		m_curTime = offset;
	}

	postCommand(kSeek, offset);
	return 0;
}

int64_t CLitePlayer::getTime()
{
	return m_curTime / 1000;
}


int CLitePlayer::setScale(float scale)
{
	if ((scale < MIN_SCALE) || (scale > MAX_SCALE))
	{
		return EINVAL;
	}

	AVRational r = av_d2q(scale, MAX_SCALE);

	postCommand(kSetScale, r.num, r.den);

	return 0;
}

float CLitePlayer::getScale()
{
	return (float)av_q2d(m_scale);
}

int CLitePlayer::step()
{
	if (!isOpen())
	{
		return EFAULT;
	}

	postCommand(kStep);

	return 0;
}

int CLitePlayer::nextKeyFrame()
{
	if (!isOpen())
	{
		return EFAULT;
	}

	postCommand(kNextKeyFrame);
	return 0;
}

float CLitePlayer::getVolume()
{
	return m_audioRender->getVolume();
}

int CLitePlayer::setVolume(float vol)
{
	m_audioRender->setVolume(vol);
	return 0;
}

bool CLitePlayer::isMute()
{
	return m_audioRender->isMute();
}

int CLitePlayer::setMute(bool muted)
{
	m_audioRender->setMute(muted);
	return 0;
}

bool CLitePlayer::isFullscreen()
{
	return m_videoRender->isFullscreen();
}

int CLitePlayer::setFullscreen(bool full)
{
	m_videoRender->setFullscreen(full);
	return 0;
}

void CLitePlayer::setVideoWnd(void* hwnd)
{
    if (isRunning())
    {
	    postCommand(kSetVideoWnd, hwnd);
    }
    else
    {
        handleSetVideoWnd(hwnd);
    }
}

void CLitePlayer::setVideoWindow(VideoWindow* window)
{
	m_videoRender->setVideoWindow(window);
}

int CLitePlayer::setRatio(int width, int height)
{
	m_videoRender->setRatio(width, height);
	return 0;
}

void CLitePlayer::getRatio(int& width, int& height)
{
	m_videoRender->getRatio(width, height);
}

int CLitePlayer::setImage(const char* filepath)
{
	if (m_videoRender)
	{
		m_videoRender->setBackImage(filepath);
	}

	return 0;
}


int CLitePlayer::snap(const char* filepath)
{
	postCommand(kSnap, filepath);

	return 0;
}


int CLitePlayer::setVideoFilter(const char* filterString)
{
	postCommand(kSetVideoFilter, filterString);

	return 0;
}


int CLitePlayer::addOverlay(int idx, Overlay& osd)
{
	return 0;
}

int CLitePlayer::removeOverlay(int idx)
{
	return 0;
}

void CLitePlayer::clearOverlay()
{
}

void CLitePlayer::setOverlay(const Overlay* osds, size_t size)
{
}


void CLitePlayer::setPacketSink(MediaPacketSink* pSink)
{
	comn::AutoCritSec lock(m_cs);
	m_packetSink = pSink;
}

void CLitePlayer::setEventSink(MediaEventSink* pSink)
{
	comn::AutoCritSec lock(m_cs);
	m_eventSink = pSink;
}


size_t CLitePlayer::getProp(const char* name, char* value, size_t size)
{
	if ((!value) || (size == 0))
	{
		return -1;
	}

	std::string str;
	if (!m_params.get(name, str))
	{
		return 0;
	}

	return comn::copyStr(value, size, str);
}

bool CLitePlayer::setProp(const char* name, const char* value)
{
	if (!name || !value)
	{
		return false;
	}

    m_params.set(name, value);

	postCommand(kSetProp, name, value);
    
	return true;
}

bool CLitePlayer::existProp(const char* name)
{
	if (!name)
	{
		return false;
	}
	return m_params.exists(name);
}


int CLitePlayer::togglePlay()
{
	int state = getState();
	if (state == STATE_PLAYING)
	{
		pause();
	}
	else if (state == STATE_PAUSED)
	{
		play();
	}
	return 0;
}

int CLitePlayer::toggleMute()
{
	bool muted = isMute();
	return setMute(!muted);
}

int CLitePlayer::toggleFull()
{
	bool fullscreen = isFullscreen();
	return setFullscreen(!fullscreen);
}

int CLitePlayer::slow()
{
	float scale = getScale();
	scale -= STEP_SCALE;
	if (scale < MIN_SCALE)
	{
		return EINVAL;
	}

	return setScale(scale);
}

int CLitePlayer::fast()
{
	float scale = getScale();
	scale += STEP_SCALE;
	if (scale > MAX_SCALE)
	{
		return EINVAL;
	}

	return setScale(scale);
}


int CLitePlayer::startRecord(const char* filename)
{
	if (!filename)
	{
		return EINVAL;
	}

	postCommand(kStartRecord, filename);
	return 0;
}

void CLitePlayer::stopRecord()
{
	postCommand(kStopRecord);
}

bool CLitePlayer::isRecording()
{
	return (m_fileSink.get() != NULL);
}


bool CLitePlayer::getMetaData(const std::string& key, std::string& value)
{
	bool found = false;
	comn::AutoCritSec lock(m_cs);
	if (m_source)
	{
		found = m_source->getMetaData(key, value);
	}
	return found;
}


int CLitePlayer::open(const MFormat& fmt)
{
    m_inputSource.reset(new InputMediaSource());
    m_inputSource->open(fmt);

    util::CommandThread::start();

    postCommand(kOpen);

	return 0;
}

bool CLitePlayer::write(MPacket& pkt)
{
    if (!m_inputSource)
    {
        return false;
    }

    return m_inputSource->write(pkt);
}

void CLitePlayer::writeEnd()
{
	// pass
}


void CLitePlayer::flush()
{
	postCommand(kFlush);

	if (m_inputSource)
	{
	    m_inputSource->flush();
	}
}


void CLitePlayer::onCommand(Command& cmd)
{
	if (cmd.type == kOpen)
	{
		handleOpen();
	}
	else if (cmd.type == kClose)
	{
		handleClose();
	}
	else if (cmd.type == kPlay)
	{
		handlePlay();
	}
	else if (cmd.type == kPause)
	{
		handlePause();
	}
	else if (cmd.type == kStop)
	{
		handleStop();
	}
	else if (cmd.type == kSeek)
	{
		int64_t offset = cmd.at(0).toLong();
		handleSeek(offset);
	}
	else if (cmd.type == kSetScale)
	{
        AVRational scale;
        scale.num = cmd.at(0).toInt();
        scale.den = cmd.at(1).toInt();

		handleSetScale(scale);

		fireMediaEvent(MEDIA_EVENT_SCALE, 0);
	}
	else if (cmd.type == kStep)
	{
		handleStep();
	}
	else if (cmd.type == kNextKeyFrame)
	{
		handleNextKeyFrame();
	}
	else if (cmd.type == kSetVideoWnd)
	{
		void* hwnd = cmd.at(0).toPtr();
		handleSetVideoWnd(hwnd);
	}
	else if (cmd.type == kSnap)
	{
		std::string filePath = cmd.at(0).toString();
		handleSnap(filePath);
	}
	else if (cmd.type == kFlush)
	{
		handleFlush();
	}
	else if (cmd.type == kSetVideoFilter)
	{
		std::string str = cmd.at(0).toString();
		handleSetVideoFilter(str);
	}
	else if (cmd.type == kSetProp)
	{
		std::string key = cmd.at(0).toString();
		std::string value = cmd.at(1).toString();
		handleSetProp(key, value);
	}
	else if (cmd.type == kStartRecord)
	{
		std::string filePath = cmd.at(0).toString();
		handleStartRecord(filePath);
	}
	else if (cmd.type == kStopRecord)
	{
		handleStopRecord();
	}
}

void CLitePlayer::onIdle()
{
	if (!m_source)
	{
		timedwait(-1);
		return;
	}

    if (m_state != STATE_PLAYING)
    {
        timedwait(-1);
        return;
    }

    AVPacketPtr pkt(new AVPacket(), ffmpeg::PacketDeleter());
    av_init_packet(pkt.get());

	int rc = m_source->read(*pkt.get());
	if (rc == 0)
	{
	    if (!m_isLive)
	    {
	    	int delay = computeDelay(pkt);
	    	if (delay > 0)
	    	{
	    		timedwait(delay);
	    	}
	    }

	    dispatchPacket(pkt);
	}
	else if (rc == AVERROR_EOF)
	{
		fireMediaEvent(MEDIA_EVENT_END, 0);
		timedwait(-1);
	}
    else if (rc == AVERROR(EAGAIN))
    {
        // continue;
        timedwait(50);
    }
    else
    {
        char errbuf[AV_ERROR_MAX_STRING_SIZE] = {0};
        av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, rc);

        CLog::info("CLitePlayer read error! code:%08x, err: %s, url:%s\n",
            rc, errbuf,
            m_url.c_str());

        fireMediaEvent(MEDIA_EVENT_END, 0);
        timedwait(-1);
    }
}

bool CLitePlayer::startup()
{
	return true;
}

void CLitePlayer::cleanup()
{
	while (fetchAndHandle())
	{
		// pass
	}
}

void CLitePlayer::doStop()
{
    util::CommandThread::doStop();

    if (m_source)
    {
        m_source->interrupt();
    }
}

void CLitePlayer::onDecodeFrame(AVFramePtr frame)
{
	bool isVideoKeyFrame = false;
	if (MediaHelper::isVideo(frame))
	{
        m_videoFrameCount ++;

		isVideoKeyFrame = frame->key_frame > 0;
        if (isVideoKeyFrame)
        {
            m_format.m_width = frame->width;
            m_format.m_height = frame->height;

		    m_waitForKeyFrame = false;
        }

		filterVideoFrame(frame);
		m_videoRender->write(frame);
	}
	else if (MediaHelper::isAudio(frame))
	{
		m_audioRender->write(frame);
	}

	fireMediaFrame(frame);

	if (isVideoKeyFrame && m_format.hasAudio())
	{
		TimePoint vTime;
		TimePoint aTime;

		bool getTime = m_videoRender->getTimePoint(vTime.m_clock, vTime.m_pts);
		getTime &= m_audioRender->getTimePoint(aTime.m_clock, aTime.m_pts);

		if (getTime)
		{
			vTime.rescale(1, 1000);
			aTime.rescale(1, 1000);
			int offset = (int)vTime.getOffset(aTime);
			if (abs(offset) > 200)
			{
				CLog::warning("av time offset is too big. video(%d,%d), audio(%d,%d), offset:%d\n",
						(int)vTime.m_clock, (int)vTime.m_pts,
						(int)aTime.m_clock, (int)aTime.m_pts,
						offset);
			}
		}
	}
}

void CLitePlayer::handleOpen()
{
    if (m_inputSource.get())
    {
        fireMediaEvent(MEDIA_EVENT_OPENING, 0);

        m_source = m_inputSource;

        fireMediaEvent(MEDIA_EVENT_OPENED, 0);
    }
    else
    {
        util::ParamSet srcParams;
        m_params.filter(MEDIA_SOURCE_PROP, srcParams);

        m_source = createSource(m_url, std::string());
        if (!m_source)
        {
            CLog::error("source is null.\n");
            return;
        }

        fireMediaEvent(MEDIA_EVENT_OPENING, 0);

        std::string params = srcParams.toString(util::ParamSet::SEPERATOR, util::ParamSet::SEG);
        int rc = m_source->open(m_url, params);
        if (rc != 0)
        {
            fireMediaEvent(MEDIA_EVENT_OPEN_FAILED, 0);
            m_source.reset();
            return;
        }

        fireMediaEvent(MEDIA_EVENT_OPENED, 0);
    }

    handleOpenSource();
}

void CLitePlayer::handleOpenSource()
{
    int rc = 0;

    m_source->getFormat(m_format);
    //m_fmt = m_format;

    m_isLive = m_source->isLive();

    fireMediaEvent(MEDIA_EVENT_FORMAT_READY, 0);
    fireMediaFormat(m_format);

    m_videoDecoder.reset(new CVideoDecoder(this));
    m_audioDecoder.reset(new CAudioDecoder(this));

    if (m_format.m_codec != 0)
    {
        rc = m_videoDecoder->open(m_format);
        if (rc != 0)
        {
            fireMediaEvent(MEDIA_EVENT_OPEN_FAILED, 0);

            doCloseFile();
            return ;
        }
    }

    if (m_format.m_audioCodec != 0)
    {
        rc = m_audioDecoder->open(m_format);
        if (rc != 0)
        {
            fireMediaEvent(MEDIA_EVENT_OPEN_FAILED, 0);

            doCloseFile();
            return ;
        }
    }

    RawMediaFormat rawFormat;
    m_videoDecoder->getOutFormat(rawFormat);
    m_audioDecoder->getOutFormat(rawFormat);

    m_videoRender->open();

    if (rawFormat.m_channels > 0)
    {
        rc = m_audioRender->open(rawFormat.m_sampleRate, rawFormat.m_channels, rawFormat.m_sampleFormat);
    }

    std::string liveMode = comn::StringCast::toString(m_isLive);
    m_videoRender->setProp(MEDIA_PLAYER_PROP_LIVE_MODE, liveMode);
    m_audioRender->setProp(MEDIA_PLAYER_PROP_LIVE_MODE, liveMode);

    setupFilter();
}

void CLitePlayer::handleClose()
{
	doCloseFile();
}

void CLitePlayer::handlePlay()
{
    if (!m_source)
    {
        return;
    }

	m_videoRender->play();
	m_audioRender->play();

    m_source->play();

	setState(STATE_PLAYING);

	fireMediaEvent(MEDIA_EVENT_PLAYING, 0);
}

void CLitePlayer::handlePause()
{
    if (!m_source)
    {
        return;
    }

	setState(STATE_PAUSED);

	m_videoRender->pause();
	m_audioRender->pause();

	m_source->pause();

	fireMediaEvent(MEDIA_EVENT_PAUSED, 0);
}

void CLitePlayer::handleStop()
{
	setState(STATE_STOPPED);

	m_videoRender->stop();
	m_audioRender->stop();

    if (m_source)
    {
	    m_source->stop();
    }

	fireMediaEvent(MEDIA_EVENT_STOPPED, 0);

	/// 停止录像
    if (m_fileSink)
    {
        m_fileSink->close();
	    m_fileSink.reset();
    }

	for (size_t i = 0; i < ARRAY_SIZE(m_lastPoint); i ++)
	{
		m_lastPoint[i].reset();
	}

	m_scale = av_make_q(1,1);
	m_waitForKeyFrame = false;
    m_videoFrameCount = 0;
    m_curTime = 0;
}

void CLitePlayer::handleSeek(int64_t offset)
{
	fireMediaEvent(MEDIA_EVENT_SEEKING, 0);

	int rc = m_source->seek(offset);
	if (rc != 0)
	{
		CLog::warning("failed to seek. offset:%d", (int)offset);
	}

	fireMediaEvent(MEDIA_EVENT_SEEKED, 0);

	handleFlush();

}

void CLitePlayer::handleSetScale(AVRational scale)
{
	for (size_t i = 0; i < ARRAY_SIZE(m_lastPoint); i ++)
	{
		m_lastPoint[i].reset();
	}

	m_scale = scale;

	m_videoRender->setScale(m_scale);
	m_audioRender->setScale(m_scale);
}

void CLitePlayer::handleStep()
{
	if (m_state == STATE_PLAYING)
	{
		handlePause();
	}

	m_videoRender->step();

	TimePoint tp;
	if (m_videoRender->getTimePoint(tp.m_clock, tp.m_pts))
	{
		m_audioRender->dropTo(tp.m_pts);
	}

	readUntilVideoFrame();
}

void CLitePlayer::handleNextKeyFrame()
{
	if (m_state == STATE_PLAYING)
	{
		handlePause();
	}

	m_videoRender->nextKeyFrame();

	TimePoint tp;
	if (m_videoRender->getTimePoint(tp.m_clock, tp.m_pts))
	{
		m_audioRender->dropTo(tp.m_pts);
	}

	m_waitForKeyFrame = true;

	readUntilVideoKeyFrame();
}

void CLitePlayer::handleSetVideoWnd(void* hwnd)
{
	m_videoRender->setVideoWnd(hwnd);
}


void CLitePlayer::handleSnap(const std::string& filePath)
{
	m_snapFilePath = filePath;
}

void CLitePlayer::handleFlush()
{
	m_videoDecoder->flush();
	m_audioDecoder->flush();
	m_videoRender->flush();
	m_audioRender->flush();

}

void CLitePlayer::handleStartRecord(const std::string& filePath)
{
	if (!m_fileSink)
	{
		m_fileSink.reset(new CFileMediaSink());
        m_fileSink->open(filePath.c_str());

		if (m_format.isValid())
		{
			m_fileSink->onMediaFormat(m_format);
		}
	}
    else
    {
        m_fileSink->setFile(filePath.c_str());
    }
}

void CLitePlayer::handleStopRecord()
{
	m_fileSink.reset();
}


void CLitePlayer::handleSetVideoFilter(const std::string& str)
{
	m_vfilterString = str;

	if (m_vfilterString.empty())
	{
		if (m_videoFilter)
		{
			m_videoFilter->close();
			m_videoFilter.reset();
		}
	}
	else
	{
		m_videoFilter.reset(new CVideoFrameFilter());
		int rc = m_videoFilter->open(m_vfilterString);
		if (rc != 0)
		{
			m_videoFilter->close();
			m_videoFilter.reset();
		}
	}
}

void CLitePlayer::handleSetProp(std::string& key, std::string& value)
{
	std::string sec;
	std::string subName;

	comn::StringUtil::split(key, comn::IniProperties::SEPERATOR, sec, subName);
	if (sec == MEDIA_VIDEO_PROP)
	{
		m_videoRender->setProp(key, value);
	}
	else if (sec == MEDIA_AUDIO_PROP)
	{
		m_audioRender->setProp(key, value);
	}
	else if (sec == MEDIA_VFILTER_PROP)
	{
		handleSetVideoFilter(value);
	}
    else if (sec == MEDIA_PLAYER_PROP)
    {
        if (key == MEDIA_PLAYER_PROP_RECORD)
        {
            handleStartRecord(value);
        }
        else
        {
            m_videoRender->setProp(key, value);
            m_audioRender->setProp(key, value);
        }
    }
}


void CLitePlayer::fireMediaEvent(int type, int64_t value)
{
    CLog::debug("media event. type:%d. name:%s\n", type, Utility::getEventName(type));

	{
		comn::AutoCritSec lock(m_cs);

		if (m_eventSink)
		{
			m_eventSink->onMediaEvent(this, type, value);
		}
	}

	if (type == STREAM_EVENT_END)
	{
		comn::AutoCritSec lock(m_cs);

		if (m_fileSink)
		{
			m_fileSink->onMediaEvent(type);
		}

	}
}

void CLitePlayer::fireMediaFrame(AVFramePtr& frame)
{
//	const char* name = (frame->width > 0) ? "video" : "audio";
//    CLog::debug("dec frame(%s). pts:%I64d, duration:%d, key:%d. pkt dts:%I64d\n",
//			name, frame->pts, (int)frame->pkt_duration, frame->key_frame, frame->pkt_dts/1000);

	comn::AutoCritSec lock(m_cs);

	if (!m_snapFilePath.empty() && (frame->width > 0))
	{
		AVFrameUtil::saveFrame(frame.get(), m_snapFilePath.c_str());
		m_snapFilePath.clear();
	}
}

void CLitePlayer::fireMediaFormat(const MediaFormat& fmt)
{
	comn::AutoCritSec lock(m_cs);

	if (m_fileSink)
	{
		m_fileSink->onMediaFormat(fmt);
	}

    if (m_packetSink)
    {
        m_packetSink->onMediaFormat(m_fmt);
    }
}

void CLitePlayer::fireMediaPacket(AVPacketPtr& pkt)
{
	int64_t dist = pkt->pts - m_curTime;

	m_curTime = pkt->pts;

	if (dist/1000 > 500)
	{
		const char* name = (pkt->stream_index == 0) ? "video" : "audio";
		CLog::debug("CLitePlayer(%p) packet(%s). duration:%d, dts:%I64d, pts:%I64d, dist:%d.\n",
				this, name, (int)pkt->duration/1000, pkt->dts/1000, pkt->dts/1000, (int)(dist/1000));
	}

	comn::AutoCritSec lock(m_cs);
	if (m_packetSink)
	{
		MPacket mpkt;
		memset(&mpkt, 0, sizeof(mpkt));
		mpkt.type = pkt->stream_index;
		mpkt.data = pkt->data;
		mpkt.size = pkt->size;
		mpkt.duration = pkt->duration;
		mpkt.pts = pkt->pts;
		mpkt.flags = pkt->flags;

		m_packetSink->onMediaPacket(mpkt);
	}

	if (m_fileSink)
	{
		m_fileSink->onMediaPacket(pkt);
	}

}


void CLitePlayer::setSourceFactory(MediaSourceFactoryPtr& factory)
{
	m_sourceFactory = factory;
}

MediaSourcePtr CLitePlayer::createSource(const std::string& url, const std::string& params)
{
	MediaSourcePtr source;
	MediaSourceFactoryPtr factory = LitePlayerCore::getMediaSourceFactory();
	if (factory)
	{
		source.reset(factory->create(url, params), MediaSourceDeleter(factory));
	}

	if (!source)
	{
		if (m_sourceFactory)
		{
			source.reset(m_sourceFactory->create(url, params), MediaSourceDeleter(m_sourceFactory));
		}
	}
	return source;
}

void CLitePlayer::doCloseFile()
{
	//m_videoRender->close();

	if (m_audioRender)
	{
		m_audioRender->close();
	}

	if (m_videoDecoder)
	{
		m_videoDecoder->close();
		m_videoDecoder.reset();
	}

	if (m_audioDecoder)
	{
		m_audioDecoder->close();
		m_audioDecoder.reset();
	}

	if (m_source)
	{
		m_source->close();

		fireMediaEvent(MEDIA_EVENT_CLOSED, 0);

		comn::AutoCritSec lock(m_cs);
        m_source.reset();
	}

	cleanupFilter();

	m_isLive = false;
    
}

void CLitePlayer::setState(StreamState state)
{
	comn::AutoCritSec lock(m_cs);
	m_state = state;
}

void CLitePlayer::setupFilter()
{
	if (!m_vfilterString.empty())
	{
		m_videoFilter.reset(new CVideoFrameFilter());
		int rc = m_videoFilter->open(m_vfilterString);
		if (rc != 0)
		{
			m_videoFilter->close();
			m_videoFilter.reset();
		}
	}
}

void CLitePlayer::cleanupFilter()
{
	m_videoFilter.reset();
}

void CLitePlayer::filterVideoFrame(AVFramePtr& frame)
{

	if (m_videoFilter)
	{
		AVFramePtr outFrame(av_frame_alloc(), ffmpeg::AVFrameDeleter());
		if (0 == m_videoFilter->filter(frame.get(), outFrame.get()))
		{
			frame = outFrame;
		}
	}
}

void CLitePlayer::dispatchPacket(AVPacketPtr& pkt)
{
	fireMediaPacket(pkt);

	if (pkt->stream_index == MEDIA_TYPE_VIDEO)
	{
		if (m_videoDecoder)
		{
			m_videoDecoder->input(pkt);
		}
		else
		{
			CLog::warning("no video decoder.\n");
		}
	}
	else if (pkt->stream_index == MEDIA_TYPE_AUDIO)
	{
		if (m_audioDecoder)
		{
			m_audioDecoder->input(pkt);
		}
		else
		{
			CLog::warning("no audio decoder.\n");
		}
	}
}

int CLitePlayer::computeDelay(AVPacketPtr& pkt)
{
	int64_t delay = 0;
	int64_t curClock = getClock();
	TimePoint curPoint(curClock, pkt->pts);
	TimePoint& lastPoint = m_lastPoint[pkt->stream_index];

	if (pkt->stream_index != 0)
	{
		delay = 0;
	}

	if (!lastPoint.isSet())
	{
		lastPoint = curPoint;
	}
	else
	{
		int64_t ptsDelta = curPoint.m_pts - lastPoint.m_pts;
		int64_t clockDelta = curPoint.m_clock - lastPoint.m_clock;
		if (ptsDelta > MAX_SYNC_TIME)
		{
			lastPoint.reset();
		}
		else if (ptsDelta < 0)
		{
			lastPoint.reset();
		}
		else
		{
            int64_t pretime = av_rescale_q(ptsDelta, PREREAD_SCALE, m_scale);
			delay = (ptsDelta - clockDelta) - pretime;

			lastPoint.reset(lastPoint.m_clock + ptsDelta, curPoint.m_pts);
		}
	}

	//const char* name = (pkt->stream_index == 0) ? "video" : "audio";
	//CLog::debug("computeDelay. packet(%s) clock:%I64d, pts:%I64d, delay:%d\n", name, curPoint.m_clock/1000, curPoint.m_pts/1000, delay/1000);

	return (int)delay/1000;
}

bool CLitePlayer::readUntilVideoFrame()
{
	bool found = false;
	while (!found)
	{
		AVPacketPtr pkt(new AVPacket(), ffmpeg::PacketDeleter());
		av_init_packet(pkt.get());

		int rc = m_source->read(*pkt.get());
		if (rc == 0)
		{
			if (pkt->stream_index == MEDIA_TYPE_VIDEO)
			{
				found = true;
			}

			dispatchPacket(pkt);
		}
		else if (rc == AVERROR_EOF)
		{
			break;
		}
	}

	return	found;
}

bool CLitePlayer::readUntilVideoKeyFrame()
{
	bool found = false;
	while (m_waitForKeyFrame)
	{
		AVPacketPtr pkt(new AVPacket(), ffmpeg::PacketDeleter());
		av_init_packet(pkt.get());

		int rc = m_source->read(*pkt.get());
		if (rc == 0)
		{
            if (pkt->stream_index == MEDIA_TYPE_VIDEO)
            {
                if ((pkt->flags & AV_PKT_FLAG_KEY) != 0)
                {
                    dispatchPacket(pkt);
                    found = true;
                }
            }

		}
		else if (rc == AVERROR_EOF)
		{
			break;
		}
	}

	return	found;
}

int64_t CLitePlayer::getClock()
{
    int64_t clk = util::TimeHelper::getClock();
    if ((m_scale.num > 0) && (m_scale.num != m_scale.den))
    {
        clk = av_rescale(clk, m_scale.num, m_scale.den);
    }
    return clk;
}



} /* namespace av */
